package com.fulu.javabase.juc;

import lombok.extern.log4j.Log4j2;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

@Log4j2
public class CompletableFutureTest {

    /**
     * 当使用CompletableFuture进行多级异步操作时，若采用传统的嵌套回调模式，代码会迅速失控：
     * 问题诊断：
     *
     * 缩进失控：每层回调增加一级缩进，形成"箭头代码"
     * 异常处理分散：每个回调需单独处理异常
     * 上下文丢失：多层嵌套后原始参数难以追溯
     * @param args
     */
    public static void main(String[] args) {
        // 问题代码
        problem();

        // 结构化解决方案
        // 链式组合
        chainCombination();


        // 处理需要合并多个异步结果的场景
        multipleCombine();


        // 全局异常处理
        globalExceptionHandle();

        //上下文传递，封装领域对象
        domainObject();

        // 构建异步流水线
        pipeline();

    }

    private static void pipeline() {
       /* CompletionStage<Void> pipeline = CompletableFuture.supplyAsync(this::fetchOrder)
                .thenApplyAsync(this::enrichOrder)
                .thenApplyAsync(this::validateOrder)
                .thenAcceptAsync(this::persistOrder);*/

//        pipeline.toCompletableFuture().join();
    }

    private static void domainObject() {
        CompletableFuture.supplyAsync(() -> {
                    OrderContext ctx = new OrderContext();
                    ctx.order = fetchOrder();
                    return ctx;
                })
                .thenApplyAsync(ctx -> {
                    ctx.inventory = checkInventory(ctx.order);
                    return ctx;
                })
                .thenApplyAsync(ctx -> {
                    ctx.price = calculatePrice(ctx.inventory);
                    return ctx;
                });
    }

    static class OrderContext {
        Order order;
        Inventory inventory;
        Price price;
    }

    static class Price{

    }

    static class Order{

    }

    private static void globalExceptionHandle() {
        CompletableFuture.supplyAsync(() -> fetchOrder())
                .handle((order, ex) -> {
                    if (ex != null) {
                        log.error("Fetch order failed", ex);
                        return defaultOrder();
                    }
                    return order;
                })
                .thenApplyAsync(order -> checkInventory(order))
                .exceptionally(ex -> {
                    log.error("Check inventory failed", ex);
                    return fallbackInventory();
                });
    }

    private static void multipleCombine() {
        CompletableFuture<Inventory> inventoryFuture = getInventory();
        CompletableFuture<PriceRule> priceRuleFuture = getPriceRule();

        inventoryFuture.thenCombine(priceRuleFuture, (inventory, rule) ->
                        calculateFinalPrice(inventory, rule))
                .thenAccept(price ->sendNotification(price));
    }

    private static void chainCombination() {
        CompletableFuture.supplyAsync(() -> fetchOrder())
                .thenCompose(order ->
                        CompletableFuture.supplyAsync(() -> checkInventory(order)))
                .thenCompose(inventory ->
                        CompletableFuture.supplyAsync(() -> calculatePrice(inventory)))
                .thenAccept(price ->
                        CompletableFuture.runAsync(() -> sendNotification(price)));
    }

    private static void problem() {
        // 问题代码
        CompletableFuture.supplyAsync(() -> fetchOrder())
                .thenAccept(order -> {
                    CompletableFuture.supplyAsync(() -> checkInventory(order))
                            .thenAccept(inventory -> {
                                CompletableFuture.supplyAsync(() -> calculatePrice(inventory))
                                        .thenAccept(price -> {
                                            CompletableFuture.runAsync(() -> sendNotification(price));
                                        });
                            });
                });
    }

    private static Inventory fallbackInventory() {
        return null;
    }

    private static Object defaultOrder() {
        return null;
    }

    private static Object calculateFinalPrice(Inventory inventory, PriceRule rule) {
        return null;
    }

    private static CompletableFuture<PriceRule> getPriceRule() {
        return null;
    }

    private static CompletableFuture<Inventory> getInventory() {
        return null;
    }

    private static void sendNotification(Object price) {

    }

    private static Price calculatePrice(Object inventory) {
        return null;
    }

    private static Inventory checkInventory(Object order) {
        return null;
    }

    private static Order fetchOrder() {
        return null;
    }
}
